import time

from selenium.common.exceptions import TimeoutException, ElementClickInterceptedException, \
    ElementNotInteractableException
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.common.action_chains import ActionChains

from ...assistant import FwVariables
from ...exception.TestException import TestException
from ..ui.Selector import Selector
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from robot.api import logger
from selenium.webdriver.remote.webelement import *
import traceback

from ...exception.errorcode import ErrorCode

_LOCATOR_MAP = {'css': By.CSS_SELECTOR,
                'id_': By.ID,
                'name': By.NAME,
                'xpath': By.XPATH,
                'link_text': By.LINK_TEXT,
                'partial_link_text': By.PARTIAL_LINK_TEXT,
                'tag_name': By.TAG_NAME,
                'class_name': By.CLASS_NAME,
                }

POLL_FREQUENCY = 0.5


class PageElement(object):
    _driver = None
    _selector_type = None
    _selector_str = None
    _jqElementStr = None
    _ready = False

    '''
    子类需要自己定义， 字段代表组件渲染后的data-ui-role属性值
    '''
    _fish_data_ui_role = None

    def __init__(self, driver: WebDriver, selector_str, selector_type=Selector.CLASS_NAME):
        self._driver = driver
        self._selector_type = selector_type
        self._selector_str = selector_str
        if selector_type not in Selector.SETS:
            raise TestException.by_error_code(ErrorCode.NOT_SUPPORT_SELECTOR_TYPE, value=selector_type)
        self._wait_time_out = FwVariables.get_wait_time_out()

    def get_webdriver(self) -> WebDriver:
        return self._driver

    def get_element_size(self):
        return len(self._get_web_elements())

    def wait_element_ready(self, timeout=None, poll_frequency=POLL_FREQUENCY, recheck=True):
        logger.info('Waiting element ready: {0},{1}'.format(Selector.MAP[self._selector_type], self._selector_str))
        timeout = timeout or self._wait_time_out
        WebDriverWait(self._driver, timeout, poll_frequency).until(
            EC.element_to_be_clickable((Selector.MAP[self._selector_type], self._selector_str)))

    def wait_element_visible(self, timeout=None, poll_frequency=POLL_FREQUENCY, recheck=True):
        logger.info('Waiting element located: {0},{1}'.format(Selector.MAP[self._selector_type], self._selector_str))
        timeout = timeout or self._wait_time_out
        WebDriverWait(self._driver, timeout, poll_frequency).until(
            EC.visibility_of_any_elements_located((Selector.MAP[self._selector_type], self._selector_str)))

    def wait_element_presence(self, timeout=None, poll_frequency=POLL_FREQUENCY, recheck=True):
        logger.info('Waiting element presence {0},{1}'.format(Selector.MAP[self._selector_type], self._selector_str))
        timeout = timeout or self._wait_time_out
        WebDriverWait(self._driver, timeout, poll_frequency).until(
            EC.presence_of_element_located((Selector.MAP[self._selector_type], self._selector_str)))

    def wait_text_ready(self, timeout=None, poll_frequency=POLL_FREQUENCY, recheck=True):
        logger.info('Waiting element ready {0},{1}'.format(Selector.MAP[self._selector_type], self._selector_str))
        timeout = timeout or self._wait_time_out
        WebDriverWait(self._driver, timeout, poll_frequency).until(
            EC.text_to_be_present_in_element((Selector.MAP[self._selector_type], self._selector_str)))

    def click(self):
        """
        具备clickable的元素，通过组件的click函数执行，此处click用于执行span等不具备clickable的属性点击
        :return:
        """
        try:
            self.wait_element_presence()
            # self.wait_element_ready()
        except Exception as re:
            logger.debug('element not presence, continue click, exception: {0}'.format(str(re)))

        # Modified by james at 20201120
        # FOR 适配有些页面有庶罩点不到的情况，但是点没点成功就不一定了
        try:
            self._get_web_element().click()
        except ElementClickInterceptedException as e:
            logger.debug('Click Intercepted Exception, Trying run script method, exception: {0}'.format(str(e)))
            self._execute_script("arguments[0].click()", self._get_web_element())
        except ElementNotInteractableException as e:
            logger.debug('Click Intercepted Exception, Trying run script method, exception: {0}'.format(str(e)))
            self._execute_script("arguments[0].click()", self._get_web_element())


    def doubleClick(self):
        """
        具备clickable的元素，通过组件的click函数执行，此处click用于执行span等不具备clickable的属性点击
        :return:
        """
        try:
            self.wait_element_presence()
        except Exception as re:
            print(re)

        # Modified by james at 20201120
        # FOR 适配有些页面有庶罩点不到的情况，但是点没点成功就不一定了
        try:
            ActionChains(self._driver).double_click(self._get_web_element()).perform()
            # self._get_web_element().click()
        except Exception as e:
            logger.error("Click Intercepted Exception, Trying run script method\n" + str(e))
            # self._execute_script("arguments[0].click()", self._get_web_element())

    def setValue(self, params):
        """缺省的setValue, 使用sendKeys"""
        try:
            self.wait_element_presence()
            self.wait_element_ready()
        except Exception as re:
            print(re)
        self._get_web_element().send_keys(params)

    def scrollIntoView(self, timeout=None, poll_frequency=POLL_FREQUENCY, recheck=True):
        # element= self._driver.find_element(Selector.MAP[self._selector_type], self._selector_str)
        timeout = timeout or self._wait_time_out
        element = WebDriverWait(self._driver, timeout, poll_frequency).until(
            EC.presence_of_all_elements_located((Selector.MAP[self._selector_type], self._selector_str)))
        self._driver.execute_script("$(arguments[0]).get(0).scrollIntoView();", element)

    def blur(self):
        # 没有jquery时会报错
        # self._execute_script("$(arguments[0]).blur()", self._get_web_element())
        self._execute_script("arguments[0].blur()", self._get_web_element())

    def jquery_input_change(self):
        """
        有jquery的时候, 触发input的change事件
        :return:
        """
        try:
            self._execute_script("$(arguments[0]).change()", self._get_web_element())
        except Exception as e:
            logger.info(e)

    def _get_web_element(self, wait_flag=False):
        if wait_flag:
            try:
                self.wait_element_ready()
            except:
                return traceback.print_exc()
        return self._driver.find_element(self._selector_type, self._selector_str)

    def _get_present_element(self, wait_flag=False):
        if wait_flag:
            try:
                self.wait_element_presence() #去掉timeout=10
            except:
                logger.error("Waiting element presence timeout.")
        return self._driver.find_element(self._selector_type, self._selector_str)

    def _get_web_elements(self, wait_flag=False):
        if wait_flag:
            self.wait_element_ready()
        return self._driver.find_elements(self._selector_type, self._selector_str)

    def _execute_script(self, script, *args):
        logger.debug("Executing JS: {0}, select type: {1}, selector: {2}".format(script, self._selector_type,
                                                                                 self._selector_str))
        result = self._driver.execute_script(script, *args)
        logger.debug("Executing JS Result: {0}".format(result))
        return result

        # 执行脚本也会报超时
        # timeout = self._wait_time_out
        # end_time = time.time() + timeout
        # while True:
        #     try:
        #         result = self._driver.execute_script(script, *args)
        #         logger.debug("Executing JS Result: {0}".format(result))
        #         return result
        #     except TimeoutException:
        #         logger.debug("Executing JS timeout try again")
        #     time.sleep(0.5)
        #     if time.time() > end_time:
        #         break
        # logger.error("Executing JS time out: {0}".format(script))
        # raise TimeoutException("Executing JS time out: {0}".format(script))

    @staticmethod
    def get_parent_element(element: WebElement) -> WebElement:
        return element.find_element(By.XPATH, "./..")

    def getValue(self):
        element = self._get_present_element(True)
        return element.text

    def checkValue(self, value):
        element = self._get_web_element(True)
        element_value = element.text.strip()
        if element_value != value:
            raise TestException(ErrorCode.VALUE_NOT_FIND,
                                value='The element text {0} not equal you except {1}'.format(element_value, value))

    def check_fish_data_role_id(self, raise_exception: bool = True, loop: int = 5, sleep: int = 0.1) -> (bool, str):
        """
        检查元素属性 data-ui-role，验证select元素是否为设定的组件
        组件需要定义_fish_data_ui_role属性
        如果不匹配，则抛异常
        :return:
        """
        match = False
        for index in range(1, loop + 1):
            data_ui_role = self._get_web_element().get_attribute('data-ui-role')
            match = data_ui_role == self._fish_data_ui_role
            logger.debug("check data-ui-role is {check}? real value is {value},  match result: {match}".format(
                check=self._fish_data_ui_role, value=data_ui_role, match=match))
            if match:
                return match, data_ui_role
            time.sleep(sleep)
        if not match and raise_exception:
            raise TestException.by_error_code(ErrorCode.ELEMENT_TYPE_NOT_MATCH, define_type=self._fish_data_ui_role,
                                              type=data_ui_role)
        return match, data_ui_role

    def __str__(self):
        return 'selector type {0} value {1} '.format(self._selector_type, self._selector_str)

    def move_curse(self):
        ActionChains(self._driver).move_to_element(self._get_present_element(wait_flag=True)).perform()

    def move_curse_with_single_click(self):
        ActionChains(self._driver).move_to_element(self._get_present_element(wait_flag=True)).click(
            self._get_present_element()).perform()

    def get_identification(self):
        """返回组件的标识符,结构如下，
        item之间是并且的关系(如class和tag），
        多个值之间是或的关系，如（name1和name2）
        如果返回空，则不匹配不上
        {"class":["name1","name2"],
        "tag":["name1,"name2"]}
        """
        return None
